<?php

declare(strict_types=1);
/**
 * This file is part of habo_new_media.
 *
 * @link     https://www.berlen.cn/
 * @contact  tech@berlen.cn
 * @license  http://gitlab.rehou.cn/habo/habo_new_media/-/blob/master/README.md
 */
namespace app\service\batch\kuaishou;

use app\enum\CommonEnum;
use app\enum\ConfigEnum;
use app\enum\KsBidEnum;
use app\enum\KuaiShouEnum;
use app\model\KsProjectBidTaskContentModel;
use app\model\KsProjectBidTaskLogModel;
use app\model\KsProjectBidTaskModel;
use app\service\batch\MediaInterface;
use app\service\batch\MediaService;
use app\service\client\AccountService;
use app\service\kuaishou\AdvertiserService;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\Exception;

class ProjectBidPushService extends MediaService implements MediaInterface
{
    /**
     * @var string 批量改价锁
     */
    protected const BATCH_KEY = ConfigEnum::CACHE_CONFIG[KuaiShouEnum::BATCH_CONFIG_LOCK_KEY]['project_bid_push'];

    /**
     * @var null|KsProjectBidTaskModel 任务模型
     */
    private ?KsProjectBidTaskModel $taskModel;

    /**
     * @var null|KsProjectBidTaskContentModel 任务内容模型
     */
    private ?KsProjectBidTaskContentModel $contentModel;

    /**
     * @var null|KsProjectBidTaskLogModel 任务日志模型
     */
    private ?KsProjectBidTaskLogModel $logModel;

    /**
     * 修改出价.
     *
     * @param KsProjectBidTaskLogModel $model 出价内容模型
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function projectBidPush(KsProjectBidTaskLogModel $model): bool
    {
        $this->logModel = $model;
        if (! $this->logModel->getKey()) {
            return self::setError('未知出价记录失败');
        }

        $this->taskModel = (new KsProjectBidTaskModel())
            ->where('id', $this->logModel->getAttr('project_bid_id'))
            ->find();
        if (! $this->taskModel instanceof KsProjectBidTaskModel || ! $this->taskModel->getKey()) {
            return self::setError('获取出价失败');
        }

        $this->contentModel = (new KsProjectBidTaskContentModel())
            ->where('id', $this->logModel->getAttr('project_content_id'))
            ->find();
        if (! $this->contentModel instanceof KsProjectBidTaskContentModel || ! $this->contentModel->getKey()) {
            return self::setError('获取出价记录失败');
        }
        if (! $this->logModel->getAttr('project_content')) {
            return self::setError('未知项目内容');
        }
        $project = json_decode($this->logModel->getAttr('project_content'), true);

        // 修改出价
        if ($this->contentModel->getAttr('type') != KsBidEnum::TASK_TYPE_FUR) {
            $project['deep_conversion_bid'] = $this->contentModel->getAttr('deep_conversion_bid') * 1000;
        }
        $project['cpa_bid'] = $this->contentModel->getAttr('cpa_bid') * 1000;
        $project['advertiser_id'] = $this->contentModel->getAttr('advertiser_id');

        // 修改出价
        $res = $this->updateProjectBid($project);
        if ($res === false) {
            if (! $this->setBidFail()) {
                return false;
            }
        } else {
            if (! $this->setBidSuccess($res)) {
                return false;
            }
        }
        // 更新任务状态
        return $this->projectBidFinish();
    }

    /**
     *  更新项目出价.
     *
     * @param array $params 组信息
     * @return bool|mixed|string
     */
    private function updateProjectBid(array $params = [])
    {
        $token = AccountService::getToken(CommonEnum::KS_MEDIA, $this->taskModel->getAttr('advertiser_id'));
        if (! $token) {
            return self::setError('token获取失败');
        }
        $headers = [
            'Access-Token' => $token,
            'Content-Type' => 'application/json',
            'K-RealUser-IP' => $this->taskModel->getAttr('ip_address'),
        ];
        unset($params['campaign_id'], $params['put_status'], $params['create_time'], $params['update_time']);
        return AdvertiserService::updateProject($headers, $params);
    }

    /**
     *  出价修改成功.
     *
     * @param mixed $res
     */
    private function setBidSuccess($res = []): bool
    {
        if (! $this->logModel instanceof KsProjectBidTaskLogModel || ! $this->logModel->getKey()) {
            return self::setError('未知组日志信息');
        }
        $this->logModel->setAttrs([
            'status' => KsProjectBidTaskLogModel::STATUS_SUCCESS,
            'error_json' => json_encode($res),
        ]);
        if (! $this->logModel->save()) {
            return self::setError('保存项目出价成功结果日志失败');
        }
        return true;
    }

    /**
     *  出价修改失败.
     */
    private function setBidFail(): bool
    {
        if (! $this->logModel instanceof KsProjectBidTaskLogModel || ! $this->logModel->getKey()) {
            return self::setError('未知组日志信息');
        }

        if (! $this->contentModel->save()) {
            return self::setError('保存项目出价错误结果信息失败');
        }

        $this->logModel->setAttrs([
            'status' => KsProjectBidTaskLogModel::STATUS_FAIL,
            'error_json' => json_encode([AdvertiserService::getError(0)]),
            'error_msg' => AdvertiserService::getError(0),
        ]);
        if (! $this->logModel->save()) {
            return self::setError('保存项目出价错误结果日志失败');
        }
        return true;
    }

    /**
     * 更新项目出价状态.
     */
    private function projectBidFinish(): bool
    {
        if (! $this->taskModel instanceof KsProjectBidTaskModel || ! $this->taskModel->getKey()) {
            return self::setError('未知项目出价修改');
        }

        if (! $this->taskModel->getAttr('is_pull')) {
            return true;
        }

        try {
            // 更新出价状态
            $waitTaskNum = (new KsProjectBidTaskLogModel())->where([
                ['project_bid_id', '=', $this->taskModel->getKey()],
                ['status', '=', KsProjectBidTaskLogModel::STATUS_WAIT],
            ])->count();
            if (! $waitTaskNum) {
                $taskNum = (new KsProjectBidTaskLogModel())->where('project_bid_id', $this->taskModel->getKey())->count();
                $taskFallNum = (new KsProjectBidTaskLogModel())->where([
                    ['project_bid_id', '=', $this->taskModel->getKey()],
                    ['status', '=', KsProjectBidTaskLogModel::STATUS_FAIL],
                ])->count();

                $this->taskModel->setAttrs([
                    'num' => $taskNum,
                    'status' => $taskFallNum > 0 ? (KsProjectBidTaskModel::STATUS_FAIL) : (KsProjectBidTaskModel::STATUS_SUCCESS),
                ]);

                if (! $this->taskModel->save()) {
                    throw new Exception('设置项目出价任务状态失败');
                }
            }

            // 更新出价类型状态
            $waitContentNum = (new KsProjectBidTaskLogModel())->where([
                ['project_content_id', '=', $this->contentModel->getKey()],
                ['status', '=', KsProjectBidTaskLogModel::STATUS_WAIT],
            ])->count();
            if (! $waitContentNum) {
                $contentNum = (new KsProjectBidTaskLogModel())->where('project_content_id', $this->contentModel->getKey())->count();
                $contentFallNum = (new KsProjectBidTaskLogModel())->where([
                    ['project_content_id', '=', $this->contentModel->getKey()],
                    ['status', '=', KsProjectBidTaskLogModel::STATUS_FAIL],
                ])->count();

                $this->contentModel->setAttrs([
                    'num' => $contentNum,
                    'status' => $contentFallNum > 0 ? (KsProjectBidTaskModel::STATUS_FAIL) : (KsProjectBidTaskModel::STATUS_SUCCESS),
                ]);

                if (! $this->contentModel->save()) {
                    throw new Exception('设置项目出价类型状态失败');
                }
            }
            return true;
        } catch (\Exception $e) {
            return self::setError($e->getMessage());
        }
    }
}
